#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

#include "Mutex.hpp"
#include "Cond.hpp"

namespace BlockQueueModule
{
    static int g_queue_capacity = 10;

    //version2
    using namespace LockModule;
    using namespace CondModule;
    template<class T>
    class BlockQueue{
    public:
        int isFull() { return _queue.size() == _cap; }
        int isEmpty() { return _queue.size() == 0; }
    public:

        BlockQueue()
            : _cond_producer_num(0),_cond_consumer_num(0)
        {}
        ~BlockQueue()
        {}

        // 生产者
        void Enqueue(const T &in)
        {
            LockGuard guard(_mutex);
            
            // 对条件进行判断，为了防止伪唤醒，我们通常使用while进行判断！
            while(isFull()) //用 while 防止 spurious wakeup问题
            {
                ++_cond_producer_num;
                _producer_cond.Wait(_mutex);
                --_cond_producer_num;
            }
            _queue.push(in);
            
            // 我们是持有锁的，push了队列肯定有数据
            if(_cond_consumer_num)
            {
                _consumer_cond.Notify();
                // usleep(100000);
            }
            // 归还锁
        }

        // 消费者
        void Dequeue(T *out)
        {
            LockGuard guard(_mutex);
            // 对条件进行判断，为了防止伪唤醒，我们通常使用while进行判断！
            while(isEmpty())
            {
                ++_cond_consumer_num;
                _consumer_cond.Wait(_mutex); // 等待的时候要归还锁，所以需要传 _mutex
                --_cond_consumer_num;
            }
            *out = _queue.front();
            _queue.pop();

            // pop了 一定有空间
            if(_cond_producer_num)
            {
                _producer_cond.Notify();
                // usleep(100000);
            }
        }
    private:
        std::queue<T> _queue;
        Mutex _mutex;

        Cond _producer_cond;
        Cond _consumer_cond;

        int _cond_consumer_num;
        int _cond_producer_num;

        int _cap = g_queue_capacity;
    };

    //version 1
    // template<class T>
    // class BlockQueue{
    // public:
    //     int isFull() { return _queue.size() == _cap; }
    //     int isEmpty() { return _queue.size() == 0; }
    // public:

    //     BlockQueue()
    //         : _cond_producer_num(0),_cond_consumer_num(0)
    //     {
    //         pthread_mutex_init(&_mutex, nullptr);
    //         pthread_cond_init(&_consumer_cond, nullptr);
    //         pthread_cond_init(&_producer_cond, nullptr);
    //     }
    //     ~BlockQueue()
    //     {
    //         pthread_mutex_destroy(&_mutex);
    //         pthread_cond_destroy(&_consumer_cond);
    //         pthread_cond_destroy(&_producer_cond);
    //     }

    //     // 生产者
    //     void Enqueue(const T &in)
    //     {
    //         pthread_mutex_lock(&_mutex);
    //         // 生产一个数据
            
    //         // 对条件进行判断，为了防止伪唤醒，我们通常使用while进行判断！
    //         while(isFull()) //用 while 防止 spurious wakeup问题
    //         {
    //             ++_cond_producer_num;
    //             pthread_cond_wait(&_producer_cond, &_mutex);
    //             --_cond_producer_num;
    //         }
    //         _queue.push(in);
            
    //         // 我们是持有锁的，push了队列肯定有数据
    //         if(_cond_consumer_num)
    //         {
    //             pthread_cond_signal(&_consumer_cond);
    //             // usleep(100000);
    //         }
    //         // 归还锁
    //         pthread_mutex_unlock(&_mutex);
    //     }

    //     // 消费者
    //     void Dequeue(T *out)
    //     {
    //         pthread_mutex_lock(&_mutex);

    //         // 对条件进行判断，为了防止伪唤醒，我们通常使用while进行判断！
    //         while(isEmpty())
    //         {
    //             ++_cond_consumer_num;
    //             pthread_cond_wait(&_consumer_cond, &_mutex); // 等待的时候要归还锁，所以需要传 _mutex
    //             --_cond_consumer_num;
    //         }
    //         *out = _queue.front();
    //         _queue.pop();

    //         // pop了 一定有空间
    //         if(_cond_producer_num)
    //         {
    //             pthread_cond_signal(&_producer_cond);
    //             // usleep(100000);
    //         }
    //         pthread_mutex_unlock(&_mutex);
    //     }
    // private:
    //     std::queue<T> _queue;
    //     pthread_mutex_t _mutex;

    //     pthread_cond_t _producer_cond;
    //     pthread_cond_t _consumer_cond;

    //     int _cond_consumer_num;
    //     int _cond_producer_num;

    //     int _cap = g_queue_capacity;
    // };


}